這邊我想講一些圖像特徵中"特徵點偵測"的部分,有很多演算法想辦法從圖像中獲取有意義的特徵,
比方說角點、物體的邊緣,一些紋理強烈的部分。而這篇提到的FAST主要在於偵測角點的部分。
演算法的細節可以參考openCV的講解。
https://docs.opencv.org/master/df/d0c/tutorial_py_fast.html
而FAST的演算法只提供找尋有意義的特徵,並沒有為該特徵計算它的描述符用以辨識不同特徵的差異性。
如果我們未來想用FAST的特徵來做特徵匹配的部分,就會需要搭配其他演算法來計算特徵的描述符。
我想把這部分留到日後的內容。
具體的程式碼由opencv的範例改寫
https://docs.opencv.org/3.4/d7/d66/tutorial_feature_detection.html
我原先想直接示範SURF的特徵點偵測,但是當初在cmake的時候似乎漏掉了 OPENCV_ENABLE_NONFREE
的選項,
有些演算法有專利,而opencv需要把這些演算法的實作搬移到當初下載sources code 的extra modules裡面。
看起來又要重新編譯sources code了呢............
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include "opencv2/features2d.hpp"
#include "opencv2/xfeatures2d.hpp"
#include <opencv2/imgproc.hpp>/*putText*/
#include <string>
int main() {
cv::Mat image01 = cv::imread("box.png", -1);
cv::imshow("box", image01);
cv::waitKey(0);
//-- Step 1: Detect the keypoints using FAST Detector
int threshold = 10;
bool nonmaxSuppression = true;
int type = cv::FastFeatureDetector::TYPE_9_16;
cv::Ptr<cv::FastFeatureDetector> detectorFAST = cv::FastFeatureDetector::create();
std::vector<cv::KeyPoint> FASTkeypoints;
detectorFAST->detect(image01, FASTkeypoints);
/*
int minHessian = 400;
cv::Ptr<cv::xfeatures2d::SURF> detector = cv::xfeatures2d::SURF::create(minHessian);
std::vector<cv::KeyPoint> keypoints;
detector->detect(image01, keypoints);
*/
//-- Draw keypoints
cv::Mat img_keypoints;
cv::drawKeypoints(image01, FASTkeypoints, img_keypoints);
cv::waitKey();
for (unsigned int j = 0; j < FASTkeypoints.size(); j+=100) {
std::string kpPosition = std::to_string(j)+"th Scale: " + std::to_string(image01.at<uchar>(cv::Point(FASTkeypoints.at(j).pt.x, FASTkeypoints.at(j).pt.y)));
cv::putText(img_keypoints, kpPosition,cv::Point(FASTkeypoints.at(j).pt.x, FASTkeypoints.at(j).pt.y),0,0.5, cv::Scalar(255,255 ,255 ),1);
}
//-- Show detected (drawn) keypoints
imshow("FAST Keypoints", img_keypoints);
cv::waitKey();
return 0;
}
第一步當然就是先讀入一張影像,你可以使用你自己的影像(但是要注意FAST只適用灰階影像),
所以你可能需要改變imread的參數,或是讀入彩色影像之後再轉灰階。
也可以從sources code的資料夾裡面的samples/data找到我這邊使用的box.png 。
首先先創建偵測特徵的FAST,用cv::Ptr<>包起來,我們就不需要花心思去思考該Detector甚麼時候要delete。
而create裡面有三個可以修改的選項,不用賦值也沒關係它有預設值,這三個選項會改變FAST演算法的變數,
你可以理解完演算法再來做你想要的修改。
而Detector需要我們準備一個KeyPoint由vector包起來的容器,來儲存偵測出來的特徵點的位置,
之後我們想要將偵測出來的特徵點在原本的圖像上標記起來可以使用 cv::drawKeypoints來標記,
for (unsigned int j = 0; j < FASTkeypoints.size(); j+=100) {
std::string kpPosition = std::to_string(j)+"th Scale: " + std::to_string(image01.at<uchar>(cv::Point(FASTkeypoints.at(j).pt.x, FASTkeypoints.at(j).pt.y)));
cv::putText(img_keypoints, kpPosition,cv::Point(FASTkeypoints.at(j).pt.x, FASTkeypoints.at(j).pt.y),0,0.5, cv::Scalar(255,255 ,255 ),1);
}
最後你可以透過KeyPoint裡面的所記錄特徵點在影像中的位子(x,y),回去讀影像該特徵點的灰階值,並將結果表示在影像上。
而由於FAST 偵測出來的特徵點有很多,如果要每個特徵點的都畫出來的話,整個圖像反而變得難以辨識,
所以你可以修改for裡面的變數來讓畫面更簡潔有意義。
而未來要如何將這麼多的特徵點過濾出比較有高辨識度的點,就是圖像匹配主要的工作。
最後KeyPoint在vector的順序是有其意義的,未來再做特徵匹配的時候,會跟描述符的順序一起看。你如果想對vector裡面的資料做變動,請確定你的操作不會破壞所對應的描述符的意義。